{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 导入必要的库\n",
    "\n",
    "我们需要导入一个叫 [captcha](https://github.com/lepture/captcha/) 的库来生成验证码。\n",
    "\n",
    "我们生成验证码的字符由数字和大写字母组成。\n",
    "\n",
    "```sh\n",
    "pip install captcha numpy matplotlib torch torchvision tqdm\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:45.698786Z",
     "start_time": "2019-06-18T11:19:45.381128Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 192 64 4 37\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision.transforms.functional import to_tensor, to_pil_image\n",
    "\n",
    "from captcha.image import ImageCaptcha\n",
    "from tqdm import tqdm\n",
    "import random\n",
    "import numpy as np\n",
    "from collections import OrderedDict\n",
    "\n",
    "import string\n",
    "characters = '-' + string.digits + string.ascii_uppercase\n",
    "width, height, n_len, n_classes = 192, 64, 4, len(characters)\n",
    "n_input_length = 12\n",
    "print(characters, width, height, n_len, n_classes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 搭建数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:45.704071Z",
     "start_time": "2019-06-18T11:19:45.700019Z"
    }
   },
   "outputs": [],
   "source": [
    "class CaptchaDataset(Dataset):\n",
    "    def __init__(self, characters, length, width, height, input_length, label_length):\n",
    "        super(CaptchaDataset, self).__init__()\n",
    "        self.characters = characters\n",
    "        self.length = length\n",
    "        self.width = width\n",
    "        self.height = height\n",
    "        self.input_length = input_length\n",
    "        self.label_length = label_length\n",
    "        self.n_class = len(characters)\n",
    "        self.generator = ImageCaptcha(width=width, height=height)\n",
    "\n",
    "    def __len__(self):\n",
    "        return self.length\n",
    "    \n",
    "    def __getitem__(self, index):\n",
    "        random_str = ''.join([random.choice(self.characters[1:]) for j in range(self.label_length)])\n",
    "        image = to_tensor(self.generator.generate_image(random_str))\n",
    "        target = torch.tensor([self.characters.find(x) for x in random_str], dtype=torch.long)\n",
    "        input_length = torch.full(size=(1, ), fill_value=self.input_length, dtype=torch.long)\n",
    "        target_length = torch.full(size=(1, ), fill_value=self.label_length, dtype=torch.long)\n",
    "        return image, target, input_length, target_length"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 测试数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:45.733929Z",
     "start_time": "2019-06-18T11:19:45.705130Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "67NQ tensor([12]) tensor([4])\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAABACAIAAADDDu+IAAAZrklEQVR4nO19SWxkV5bdvW/4Q8ycmcykctSQkqulKlWV3ENVW954QBuwNjZgeOHa9MarXve61w2vvSh4ZxgG5EXBaMMLobtgdzVaWSXBslLKLCmZZA5kBMkY//DG68VnMpnMIBkRjEhlyTqIRSQy/n+P/51333n33nc/DmwG3+Hlxi/e/vPiy598+hffbE+eB35HoJccupMONlpbH95a/+DdypWloFH6pnv0DL4j0O8AdCctvrxs7IEpEuhl/iO/w+wgpnKXl9zMTgUm1zZVNlHoCBBFKRDlUJTCb7pf3zCmQKCCPV/85f8AgK0Pb938s396/nu+hLC5fnTrrtrssMwRAluI1/7oZsRQRsEpV33rDTObyl22PrxVfFn/4N2p3PAFQHfS4jPi702us73+4H5rcK812NgdbO09/PiOzfXpTQw2Wrf/8q8GG63RG/rdwnQItP7Bu+Uri2/82T95ydevQ9JMMrRE4AgsAQESuZ4ySutMEdFJbRWGOdnYPZxgLww201lnkHUGVpmZNjSFJSxolCpXloqVayrsmZHZPxRqq+/fBICvfv5LGGfNRQ9gPHoAAPAARM5YlWbOWBHIoZe8eMPsrDXKOGN1N21+eg8kX75xSYRSlgJRmolim46IHn2kzyTH6HrcJqr4IspnP5ejQm3ziD0YcWhNrk2mXKLgib1BT+BIdRO6NNwCFTd/kRsL55waZM17j7Jm1/Vyfb8Dlra+2hOBiBZqa3988+Ul0Ig4kxxj6XGbqv3/s9V48yKLJONnr8WH9uCVD94FgO2Pbo8+tDbXzdubJtdQsAUBPPh2HkSRM04OG5dzGuZxzbDVVvXT5t2tzmcP7H4KiUFNZLzbT3MOhJh3E1GPAUDIaQ76iyPQiOQYxezbRNlEDTZ2+/dbtsSWSjKsxIydwaGj9gAA5t65DCMPj8m1zZQdKE5ADJEIPJFxnQet2vriSVdNbHUmcIs4Y5p3H3Q/2XLbfcgsEJEHBCACcGhTne31e/3ewvoK1sp8ehya5EYm1846q7SIAiGECIcrgOcxCjlGMfs2Udsf3R7caznwllO8XAsr8elNT0GoeWCGAIEYACEQuFTbXNtMQ7U8vJ9PBOzojwgmdYuYTOt2YvYSTCwieIEQcOIMjQfjXTd79L+/kLVYddO1712LqqVpcWi8u9hcF7vZ1p0HLBLkaOnyatyoiFI4ihA5kxynD3NheGyi8mZvcK+VN3vAkKzHrtGdNJqvnN76eVQIOkJLaAkQfJkzRag9OG/3U6uMM/b58bDa5O1B887W/PLigdexHIpTnUaHGEt9e+etMbqfqe0upg6IRCXChVisVijkup2Yx30zMLijXCe31u6UwrU3r7xoAhU2PGsPWp9vZttdaCujDa8E6rPtysWFxR9fjxkCgIhPfEAj2oBTrE7e7G1/dPuARqkCBK+t3h189R//+tU//UeMsRkKVUesZ8ERRBwCDkCgPViyyrQftkoLtaEE2r59P/98J822eDUMrs6v/fi1EQk0lvq2Srcf7+59+cA1B6SdiAOQfOWH1+VcmdfC9qPdfn3PbLSpmVBmbTtTe32rDZTPsNkjYlQC2VQ9+vhuf6NlexntK7AePLiOcghgfLRSa999vPT9K6cQCCa1AYdWZ/uj23mzZxMFAQMOTpDrqsHdnWi5NlMPuMm0HSg7yNERBlxcqLntAaUGPblUW22sNmEpOt5tZUw/y1s96Bvf4aW5wCg9yqCNu9qaTO/ffZTcaUJfAQMLvrpWf/BfP772b/8wnK+uvXE5WZrbga/TTg7aMUcIwBAneRDDMJIjkbw3gzzf2NebHdrNwREgYMQxEhhwm+rtv/kib3ZsqqbVrUMU1Hnwi08e/OKTQ/bwRsRfXcBKmG53ioDUTB0tNlPNzzZspgEBQ8HLIcyFwBA8CM6ZHb6vISLviQgIgQQCG2PMgkap+IzyY6+M301pLwMCy8l48+C//H3e7G19eItLEZbjuFGJ6iUWCAAQgawuNsbSZKdjJAvkPalBRtqhJZQcKpJChqFg9cg+7tlWBgQmmTJ7hhgeBAqYmIvDt5ajemW/ncWvzEHmbvzp+7NztJhcZ/v9rNUrFk2QDCQDwQgRgQLii+srJ+oJIiAChrwSijg4PWo2OSyxxKHyEHK5Vh/83aasRAzZ4aTiUohA8kBYhiRZVC0BTM0CjUQgm6siEE1AVBPRjcVoseK854JnjpwFqy1UgrEm2dmNPtlqFYqHJIOQUyOQNxZXv3c1H2RBNbb1qPzOcrhSmwV7Ck9M1hk0b993iQIPgAgIPBBAAAyAQASy+Ay5/tC/iCAq4fIbr4hw+gQiInAerEdPGArZKN/4dz/d++jLo/qJS15bnu9EmwDgGOw/bJUvLUyrA2cTyGY67yS7n2065/hCSa7Vqq8sRvOV6lyNjOvXqntik4cMSyHwqRHIpipv9QYbrbzZI44YC6pLXIzjC/XK6nw8X+OM78UCpcCAs4BPq91DHHpiGr9/NX3cMZnmQIBIklnnSKCXyDUUaxQR4bOqwiptlLZKAwBwFJVIlmZigayxeZoX/gIRyvlX1xoXF5ffuQpH9JO33lsHmQMESg0wdNYd+2OLLxPMw7MJZFK1/as7am/AOYtfX1n9/vWgEolSKAQHRBkGEIt+q808FKvsVGBS1fz1PZOqgj14uVZ5ZU5WY1kOly+viUAaydAQCobGw4mxhAlx1BPT394v/eAiegICkIwYskiygFMgKHXGmzzLI23ks9bFatP6essUoVaOEDCanm59pqFMdR/v2VQBoiiF1YV6abnO+DMzyqaq+dl9myhwAAKZ5HhkrThnLtfZItomyrUz3855JVx563Jlda40Vw3CgHHOGAvK0fzacuPCYv3i4hSlGRAQEABgwP1qXL6xtPrWlbU3r164/koYR1xylSo+F4MtpMjUmj3EM54YIvSEBMCR1UMZh7X1ZRkGgGCU2d/asdnxpA6jjEuN7WTo6EBBz4Q/4FKtH3ZtbkgixoIJfow9AGBTZbtZoeGIiIjAHzyy86cMnGEzijQ811VSynCpGjUqx3YcjPMgxPm1ZQDgZwUTjuJ0sylK4dJ71x8lObMueH2psjpXnquF8cFW2XsfRqHr5kgAdgb0OeKJoZD12137oAcAKBkPZalRiaMob7TMXuJTo/cGRqnQxcdGjgzxngVPojRDBU3GY2rBOB8yEghD7ZwHdMV8BBZJ0paLp109Z8rAGQSyqWp+vmkTJeNg5XtX5LBwLuNslFjmUZxpNkUpiBaq9fcu61wtXbkYNspHicsYQ0csc2QcAEzdAh31xORpBpagaIczWQrrK/OQWlGL0BFYwpCn/X5pvnZ86lsPDoCAl4Pl12eioAEAPSERABx4ukvDWiFCS4WHOiyXVt+4wsXTh3nOlIGzCJQot5vYRMlSaK21zoJh5wznjhLrQURRDqPl+mK9KsNwSIsEgIAEM1nAjthFlWRoCYkIERhyKbgQwD22ciSAw88ROOOcts44AAKGohrPSEEfwAMw5PVo6feuyHh4QIkQgEDGwdp7r8WN8qHYOH+I8DQq2FS5fu7bOXoyWj/6/F45GyysLkVxNGLw6ySMYja5EPMrSwBw1N4edExpm2urNBDRzDgET9KATFbs4QEYEJBVmlKttSZH4LxrJoGMyD/tg1F6/8GeyRURAMNCes+oh0BACKwaxou1eKH6fCTAamOVsdqCYKIaiWp0LCvonB6QUwmUqJ3/dUd3UuecMxr2+55B8unDOIpXf3ozRpg4Qakwm6vv3wwaw+PYMIw3TztmbGvjkc8sIDA3/SXsaUO5bt6+f5C5huD5AYH2Nh8abwABjPfadbf3yiuNp1cpk+13TSdlzpNkwGeloA+AICrR8veGmx+rzO7GY5dqDDgtxxBO2eVxmnbRnVS1+u1PN22mnfe4m6svWvlmu3+/tfXXn+W91Fk7QZOF2bz+s58CwFc//5sJEs6tMgSgnAYPHnyBCXpyJkyuTaZtpoCIEAgBEL0nIvJEgAAENtUmU0c3YuQ9acPaGh0Bm6LX90TISIo4GBqINFrbXFtjRCWM1xrnWTeG4kQC6U6aN3vt32zaRGUP25g7SCzrGVDOJirb7T/+/H7eTZ2ZhEMAoDvJVz//5WS7RyY5Oc+lKAJSOp9+DO4ZeAA68AeKUEaVUn1tUcQBcQQA0jZ/1LXaePeUxOgAHRULH82aQAwB8WQvLhIQSMaWy41Ly3LaWv5EAtlEbf23Wy5RgBBfmhflMF6sRcu1oBShBdfPk9/u7H31eGICnWf3yDivrs4zIYrFK4hmeLoPn2hkJnhQj5eurcWVUqle5bWIBAAQKm/SvLe9b9RTI3Swm2bAK+HsFHSRCODAw3yIcsjaZLWxubLGkmRyucJjeYowmAzDCWRTle/2MZSEUH1zLVhvlK4vrb5/c+lH16lIk0yt7me6n5lJT42c5ySQDKWIAhkFgMA4KzBZN84AARAwByiYqEWV9cWoVpZRgIKj5MAZMATnSbnBTte7J/EB58ESWCKGohHPKAoGAN75/YctBySq0VALZJRu/vahaic+QOCIY3pbRsFwEW1T3fzNPa9M+eoSLUTB5bm1P3gjrpVbf3s3WKl6IKcNKZd2eiZTthyddK7lJIy4ezzJ2UgEQRwSAgQcOMIMnsvTliyBI5BMXp1buLJaLAEIgIL7gLHUAYHv67AaeeMAwCpjE+Ue9tB5EkzEoYjD2e3hg2qEADYdfr7RKmN6me1kshTyOJhuOn2BIY/e5DrZbqf3Wt3PH3rvWTkIlyrhXFlEwcKPrs39+Ip8dZ5XQlRO7yWtjcd2olXszJSX08/+5VlGMSOBfj4kMcNNMhae7oDzSshLQZG5gYKh5Cg5IYInJMr3+oVYNlq37jywiQZLwGCmWzAiisslInreFwVFQDdTNlHMgWyUlq6tjTvPR8EwAnXSx393t/3r+zZRppOW1+cXblwQoRTlMFqqzb9+afntK6wagvWul9tUWTWc/ufB2TEaRAIkjoDj5WqNBSwSegBIPMMDEQbRQkWWQ0QEQq+cy43NDRFZZYwyVhkYP49sfFD7QRMYynKAzy1hJtOtz++bbiZKQeXifFyvvCAC2UQlv3lgE8XjoP7O+sUfvlq/sFSs4owxEUoRhywUgMgJwbrno3dTwSkqmzEWV8sMEDxRbsDPzBHkAa0HAP8sFWQo59eWRCiJAyCgI9tOs15itQFH6AgcEUNRCcUsfdDeeV4JvffEEZ47YW0TZXZT18vEXLx4fW3EdOxxcZxARETWR8t1EQb1t9dXfvp6abURxOGhSkVAqzTGAogYZ5WlxriBMBitsMEpKptxxgE550hFktdMZvlhIBkASDI6skhwKWQUiCigkAEAGm8T3dlqmlyDI+hpsL5IRJydgi66EVRKVBb57sAqe+hHsMbm/TTvJG4vFQYrlxZEeVg4aBo4PvbOOGMtBrzy2kqwVBH1mD/LXESMyyWWWGAInB3sRMbBKIUNDlX20D2aybRNldtNgYBXwxmJjOI0apEKDQyIwVGhIeIgvFA/8N0R+NyYfq6SzCbK9lWRNi4rsZylgkbGeCxBcpfpwV7X+YNtoLO2ef/R40+/9gMdRIGMgpNiZOfHcQJZYwetvmfAYkkV4ZGInnHyemu9sl5b8ISx0Ek+1hZ69ASUU1S2zdTOJxu2n6Mnn5sZEcjkuvgAIkiG7BmnsojDhasXgkoEkhWeRhEFVpnW149sqtATMDzdDT1ufZnnwTjjoRRSOGVs8tQbfpDHvdc3ucZI1lYWpu7+edqHY//2zjky3jhiALG01iE+8xujTPPOA6MMADLA+QtL4/pgzl+zwhprjbHGkCdnHDl/Uo2VqQA58oAzydkRW8sYYwHHhVJxRoZpsspk/TTr9F2mAYAYEseT3NBTKR3EkFUa1dJSTVjs3d02g9xZZ3Jtelm21caeRs7YfCxKwdH8jeni+NhzyYsPMPSZDsvxU/8YgFU66w7yXuL6CjxJEEKIcbX9VIoJ8UZExQ5ZMjtRSG6cxjCol8qN0rG/NE0yYkV2PYHzpjXYu/fY5gasJ0TicNLiPq3SQchQxuHi2orwaFqDnS83+9v7/Ud7D3/1hd/sYWJFJYzX52Zah+84MRGxtjjX5/fREXDW2d6rrswd/q/JdeuLLdNJwZKMAhkHQ1PMTsFUigmxgFNumeRkvTMWCJy200yoPUSRdcSYiILa8uIxHRpXSqQcMQCG4Mn1c6wJ383QUxHEOCURcVqlg0RxZIcLv9fv/3Yn2+n6Tu66ue9kwJAvlebXV+Sppz3PieMEElJopbjkgGAHirw/GnK3qTZ7iWsmzHrRCBffvTY8Be5UnP8IDhGES9Xki20CcJlpP2yVF2rTJ1DhBCIgjlxIJgQ7shAwzmQkZT3MSxJSD56gKsEReAJHgCgr0SlbsCmWDiIEH6K3nja6lnfAAxhCAF6PwrlyWCvNbv2C5wnkvY/i2DsPjorAu821K1kANJnKOwPXSljqwJGoBLIcfCNlSpGhTjIKGKRkUmUSZXIdTumw91MQFJspQCQcorEot1Gt3MstWQcMKdVBIyZLQACiCKMO34JNuaYbwyIvALQ7WDIFg7LgF6srv3dthpmQRVPHO8O5sZoJDgCYGvW4217dixtVp/Xu14/2bt2zPQWenmTgjs2eKZWvI2IIHgAAU+t6Kbjp5wMhFdV1wIshakZ3UvW4+/g/33Ixx3ae7/fjucuUGmaJPHlkKNkpiRxTPAkpSkG8Und95fdT11cskqIRi9fm69dW4oXK6P7DyYZmyAYqKMUuZoRAypm9BHKb7PeS3d7e/93SD7u+mwNnfD5efO/GuNlJ06paipxH9TKWCvYTwGyStgiYA2IAAQPxzIMq/pAv/8P/TH7bHPxqo3932w2U+nzHK3dwSoQBsVl5OI9BxuHae6+t/eQNUY2II0hW+cGlC9+/sXjtohg50WXioTlOIMaehAk5IgEb2PanW63bm9uffO23ejAwgICxEK/Ug/nSWBZoilVLRSBFJEUUAKKIwmixNvqTGhFFORuTKQTASDLBjznctz68ZZS2mS6S2sBRuFSD3JLxgHiwQ5xdsu0RiDiI56vRQpXNx4Toy1wbE9ZKYSka0f1znqEZIq+Qo2jEYrGEfesGija7ndYACHBgKLc+ZKIalC/OT7B+bX14S3cSALj+/k/GvfYouOC1uUYbGTAIgmDhldWpB3psrneKbGiGTPCwWsZn3V3rH7yb/6dUvrVsm319r11aneOC+07OjScEUY5mWE1hKDiiB5DojPPWjpvod8ib1fdvFhZoxIVsyBIm4rD0ysKFP3iD1yJCgNxCR0FX+dTYVA9ub6OjWqM+wZgV9XXPD2+d15ZLEc9Xy6+vRLXy1AM9Jtc203agEFBWIxFKIZ/u8goV/Oq//8fxtUVxqVF9fVWUAu6RDSxpB2y2Z8FOApUFAID1XrlxA8yFc+76z34CYyaqDyGQjIO1t65W1xejpWq0XOP1iC3EHslo3b+zjZ6glYXVmJ8jCL/90e2JrwUAm+r9+zuBkOv//J21H4wtxUYFARbFWQJRXaixZ2VQ0CjFy/XSxTlZCTGUTIriACh6AIaiOqtqCidCMAIiwcAQaA9uDAI9e8xhvET1IRO38LfGjfLaH93c+/jr8GJ95+4DZU12d0fU43iucvXf/L6YKDi3/dHt4hzPOb1nNlWY2aUfXauuLcyKPQBABJJ76xhjqP3zE0aEsnFxMf1t0xSK58mkJ4Y407Ngw4CIrBQQeeop30p8Pl6qcbFgffVkYo8+QCdaflEK4yW49M/ezrpJqnMGrLRcd1+3r/7rfzix72v9g3e/+vkvV9+/aTrpYKM11rVBo3wYWxVREM9Xo/nqjNhjcm0TZQeKUuPLov1Xt5dWl3XleGRXBLI8Vw0Xq3YngfyI5igquZ5jC/YkzpqMfokaZOpxzyZab+4HgcRxLNAhJnBvnv2+MKu0SbXVxmdGxoEIg/OUP9addOvDjye4EABW378pGyU4kgI8gR98FNhcP/z4jr7XMb0svddigSgt1a//7Cfy+croab6/2ez8/QZ01eGWiyIeXJtb++Frcf3EY5Onw3TSYpUf/VFbY/P9ft7sc8lr11df+Zfv1l5bHbfdUVxBx37zot9YOMHcgoke6HmQdQZ5s5c3e66bcylYwMtXlobmllhjs/1+3urZdoZPPEA+4NFyNV6sTUygYxNmRNhMP/71V/Zhr3xh7tK/eKeopz5dPF8V44W+6gCeZPkAnFjdfSh0Jy1fWRqXdhMj2+8/+tsvyRE0yoB45V+9Fy/Xhv7Satt+2HS3H/CtvgwkeBDV0Mc8XJ9beWP9PM6Fo0v2iDCZ4vXo4X//tCilMHUMrYrxogk0GSaj3cToPdiNN3aopwGg+t7l6lsXaheHN22NsRVmcm1rlUq53Hjz4uD+bv3t9Wi59qL9QAAyDoNyJMoh0azOUz+fRPC7QaAXjCIKhgRYCbAk2clKizFeX5rPl7qNH9wozVeBoPEPLsFoLxCaCQSDkgAPIGdyVu55lf0dgYaDeQQ4SAY6ZT/FOAsq8fofvgkAM027GRGiFEbXF5GxWWRJDE0i+I5AQyDiMFqsovYYy2iuLE4tHvoy8OYQshSuvX0dxny9y+h4XpN9+98bP0GWQlEU2yYaJDtIXJnNmZhvAb7lBJrR+8i/9e9iHh0zK0vwEmBGr739/+FdzKPj20wgmMFrb7/ZdzG/hPiWE2gW7yN/8e9ifpnxLddAM8Iv3v7zP/n0L77pXrwU+H9xx4bzYS/0pQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=192x64 at 0x7F1FCF940C88>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset = CaptchaDataset(characters, 1, width, height, n_input_length, n_len)\n",
    "image, target, input_length, label_length = dataset[0]\n",
    "print(''.join([characters[x] for x in target]), input_length, label_length)\n",
    "to_pil_image(image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 初始化数据集生成器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:45.737300Z",
     "start_time": "2019-06-18T11:19:45.735033Z"
    }
   },
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "train_set = CaptchaDataset(characters, 1000 * batch_size, width, height, n_input_length, n_len)\n",
    "valid_set = CaptchaDataset(characters, 100 * batch_size, width, height, n_input_length, n_len)\n",
    "train_loader = DataLoader(train_set, batch_size=batch_size, num_workers=12)\n",
    "valid_loader = DataLoader(valid_set, batch_size=batch_size, num_workers=12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 搭建模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:45.744324Z",
     "start_time": "2019-06-18T11:19:45.738366Z"
    }
   },
   "outputs": [],
   "source": [
    "class Model(nn.Module):\n",
    "    def __init__(self, n_classes, input_shape=(3, 64, 128)):\n",
    "        super(Model, self).__init__()\n",
    "        self.input_shape = input_shape\n",
    "        channels = [32, 64, 128, 256, 256]\n",
    "        layers = [2, 2, 2, 2, 2]\n",
    "        kernels = [3, 3, 3, 3, 3]\n",
    "        pools = [2, 2, 2, 2, (2, 1)]\n",
    "        modules = OrderedDict()\n",
    "        \n",
    "        def cba(name, in_channels, out_channels, kernel_size):\n",
    "            modules[f'conv{name}'] = nn.Conv2d(in_channels, out_channels, kernel_size,\n",
    "                                               padding=(1, 1) if kernel_size == 3 else 0)\n",
    "            modules[f'bn{name}'] = nn.BatchNorm2d(out_channels)\n",
    "            modules[f'relu{name}'] = nn.ReLU(inplace=True)\n",
    "        \n",
    "        last_channel = 3\n",
    "        for block, (n_channel, n_layer, n_kernel, k_pool) in enumerate(zip(channels, layers, kernels, pools)):\n",
    "            for layer in range(1, n_layer + 1):\n",
    "                cba(f'{block+1}{layer}', last_channel, n_channel, n_kernel)\n",
    "                last_channel = n_channel\n",
    "            modules[f'pool{block + 1}'] = nn.MaxPool2d(k_pool)\n",
    "        modules[f'dropout'] = nn.Dropout(0.25, inplace=True)\n",
    "        \n",
    "        self.cnn = nn.Sequential(modules)\n",
    "        self.lstm = nn.LSTM(input_size=self.infer_features(), hidden_size=128, num_layers=2, bidirectional=True)\n",
    "        self.fc = nn.Linear(in_features=256, out_features=n_classes)\n",
    "    \n",
    "    def infer_features(self):\n",
    "        x = torch.zeros((1,)+self.input_shape)\n",
    "        x = self.cnn(x)\n",
    "        x = x.reshape(x.shape[0], -1, x.shape[-1])\n",
    "        return x.shape[1]\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.cnn(x)\n",
    "        x = x.reshape(x.shape[0], -1, x.shape[-1])\n",
    "        x = x.permute(2, 0, 1)\n",
    "        x, _ = self.lstm(x)\n",
    "        x = self.fc(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 测试模型输出尺寸"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:46.033594Z",
     "start_time": "2019-06-18T11:19:45.745300Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([12, 32, 37])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = Model(n_classes, input_shape=(3, height, width))\n",
    "inputs = torch.zeros((32, 3, height, width))\n",
    "outputs = model(inputs)\n",
    "outputs.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 初始化模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:48.035272Z",
     "start_time": "2019-06-18T11:19:46.034771Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Model(\n",
       "  (cnn): Sequential(\n",
       "    (conv11): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn11): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu11): ReLU(inplace)\n",
       "    (conv12): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn12): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu12): ReLU(inplace)\n",
       "    (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (conv21): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn21): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu21): ReLU(inplace)\n",
       "    (conv22): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn22): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu22): ReLU(inplace)\n",
       "    (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (conv31): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn31): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu31): ReLU(inplace)\n",
       "    (conv32): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn32): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu32): ReLU(inplace)\n",
       "    (pool3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (conv41): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn41): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu41): ReLU(inplace)\n",
       "    (conv42): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn42): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu42): ReLU(inplace)\n",
       "    (pool4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "    (conv51): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn51): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu51): ReLU(inplace)\n",
       "    (conv52): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "    (bn52): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "    (relu52): ReLU(inplace)\n",
       "    (pool5): MaxPool2d(kernel_size=(2, 1), stride=(2, 1), padding=0, dilation=1, ceil_mode=False)\n",
       "    (dropout): Dropout(p=0.25, inplace)\n",
       "  )\n",
       "  (lstm): LSTM(512, 128, num_layers=2, bidirectional=True)\n",
       "  (fc): Linear(in_features=256, out_features=37, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = Model(n_classes, input_shape=(3, height, width))\n",
    "model = model.cuda()\n",
    "model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 解码函数和准确率计算函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:48.040043Z",
     "start_time": "2019-06-18T11:19:48.036404Z"
    }
   },
   "outputs": [],
   "source": [
    "def decode(sequence):\n",
    "    a = ''.join([characters[x] for x in sequence])\n",
    "    s = ''.join([x for j, x in enumerate(a[:-1]) if x != characters[0] and x != a[j+1]])\n",
    "    if len(s) == 0:\n",
    "        return ''\n",
    "    if a[-1] != characters[0] and s[-1] != a[-1]:\n",
    "        s += a[-1]\n",
    "    return s\n",
    "\n",
    "def decode_target(sequence):\n",
    "    return ''.join([characters[x] for x in sequence]).replace(' ', '')\n",
    "\n",
    "def calc_acc(target, output):\n",
    "    output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1)\n",
    "    target = target.cpu().numpy()\n",
    "    output_argmax = output_argmax.cpu().numpy()\n",
    "    a = np.array([decode_target(true) == decode(pred) for true, pred in zip(target, output_argmax)])\n",
    "    return a.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T11:19:48.052899Z",
     "start_time": "2019-06-18T11:19:48.041088Z"
    }
   },
   "outputs": [],
   "source": [
    "def train(model, optimizer, epoch, dataloader):\n",
    "    model.train()\n",
    "    loss_mean = 0\n",
    "    acc_mean = 0\n",
    "    with tqdm(dataloader) as pbar:\n",
    "        for batch_index, (data, target, input_lengths, target_lengths) in enumerate(pbar):\n",
    "            data, target = data.cuda(), target.cuda()\n",
    "            \n",
    "            optimizer.zero_grad()\n",
    "            output = model(data)\n",
    "            \n",
    "            output_log_softmax = F.log_softmax(output, dim=-1)\n",
    "            loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n",
    "            \n",
    "            loss.backward()\n",
    "            optimizer.step()\n",
    "\n",
    "            loss = loss.item()\n",
    "            acc = calc_acc(target, output)\n",
    "            \n",
    "            if batch_index == 0:\n",
    "                loss_mean = loss\n",
    "                acc_mean = acc\n",
    "            \n",
    "            loss_mean = 0.1 * loss + 0.9 * loss_mean\n",
    "            acc_mean = 0.1 * acc + 0.9 * acc_mean\n",
    "            \n",
    "            pbar.set_description(f'Epoch: {epoch} Loss: {loss_mean:.4f} Acc: {acc_mean:.4f} ')\n",
    "\n",
    "def valid(model, optimizer, epoch, dataloader):\n",
    "    model.eval()\n",
    "    with tqdm(dataloader) as pbar, torch.no_grad():\n",
    "        loss_sum = 0\n",
    "        acc_sum = 0\n",
    "        for batch_index, (data, target, input_lengths, target_lengths) in enumerate(pbar):\n",
    "            data, target = data.cuda(), target.cuda()\n",
    "            \n",
    "            output = model(data)\n",
    "            output_log_softmax = F.log_softmax(output, dim=-1)\n",
    "            loss = F.ctc_loss(output_log_softmax, target, input_lengths, target_lengths)\n",
    "            \n",
    "            loss = loss.item()\n",
    "            acc = calc_acc(target, output)\n",
    "            \n",
    "            loss_sum += loss\n",
    "            acc_sum += acc\n",
    "            \n",
    "            loss_mean = loss_sum / (batch_index + 1)\n",
    "            acc_mean = acc_sum / (batch_index + 1)\n",
    "            \n",
    "            pbar.set_description(f'Test : {epoch} Loss: {loss_mean:.4f} Acc: {acc_mean:.4f} ')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T12:18:50.675432Z",
     "start_time": "2019-06-18T11:19:48.053976Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Epoch: 1 Loss: 3.7244 Acc: 0.0000 : 100%|██████████| 1000/1000 [01:52<00:00,  8.93it/s]\n",
      "Test : 1 Loss: 3.7294 Acc: 0.0000 : 100%|██████████| 100/100 [00:05<00:00, 17.52it/s]\n",
      "Epoch: 2 Loss: 3.7359 Acc: 0.0000 : 100%|██████████| 1000/1000 [01:52<00:00,  9.01it/s]\n",
      "Test : 2 Loss: 3.7290 Acc: 0.0000 : 100%|██████████| 100/100 [00:05<00:00, 17.65it/s]\n",
      "Epoch: 3 Loss: 3.7199 Acc: 0.0000 : 100%|██████████| 1000/1000 [01:52<00:00,  9.02it/s]\n",
      "Test : 3 Loss: 3.7271 Acc: 0.0000 : 100%|██████████| 100/100 [00:05<00:00, 16.77it/s]\n",
      "Epoch: 4 Loss: 2.3948 Acc: 0.0038 : 100%|██████████| 1000/1000 [01:52<00:00,  8.89it/s]\n",
      "Test : 4 Loss: 2.8448 Acc: 0.0015 : 100%|██████████| 100/100 [00:05<00:00, 16.95it/s]\n",
      "Epoch: 5 Loss: 0.1477 Acc: 0.8431 : 100%|██████████| 1000/1000 [01:52<00:00,  8.92it/s]\n",
      "Test : 5 Loss: 0.1622 Acc: 0.8145 : 100%|██████████| 100/100 [00:05<00:00, 17.31it/s]\n",
      "Epoch: 6 Loss: 0.0860 Acc: 0.8926 : 100%|██████████| 1000/1000 [01:52<00:00,  8.94it/s]\n",
      "Test : 6 Loss: 0.1019 Acc: 0.8745 : 100%|██████████| 100/100 [00:05<00:00, 17.48it/s]\n",
      "Epoch: 7 Loss: 0.0414 Acc: 0.9436 : 100%|██████████| 1000/1000 [01:52<00:00,  8.91it/s]\n",
      "Test : 7 Loss: 0.1066 Acc: 0.8970 : 100%|██████████| 100/100 [00:05<00:00, 25.14it/s]\n",
      "Epoch: 8 Loss: 0.0317 Acc: 0.9527 : 100%|██████████| 1000/1000 [01:52<00:00,  9.19it/s]\n",
      "Test : 8 Loss: 0.2585 Acc: 0.8132 : 100%|██████████| 100/100 [00:05<00:00, 17.50it/s]\n",
      "Epoch: 9 Loss: 0.0282 Acc: 0.9620 : 100%|██████████| 1000/1000 [01:52<00:00,  8.85it/s]\n",
      "Test : 9 Loss: 0.0775 Acc: 0.9416 : 100%|██████████| 100/100 [00:05<00:00, 17.50it/s]\n",
      "Epoch: 10 Loss: 0.0235 Acc: 0.9626 : 100%|██████████| 1000/1000 [01:52<00:00,  9.02it/s]\n",
      "Test : 10 Loss: 0.0321 Acc: 0.9519 : 100%|██████████| 100/100 [00:05<00:00, 17.20it/s]\n",
      "Epoch: 11 Loss: 0.0210 Acc: 0.9742 : 100%|██████████| 1000/1000 [01:52<00:00,  9.00it/s]\n",
      "Test : 11 Loss: 0.0268 Acc: 0.9686 : 100%|██████████| 100/100 [00:05<00:00, 17.27it/s]\n",
      "Epoch: 12 Loss: 0.0196 Acc: 0.9734 : 100%|██████████| 1000/1000 [01:52<00:00,  8.92it/s]\n",
      "Test : 12 Loss: 0.0386 Acc: 0.9555 : 100%|██████████| 100/100 [00:05<00:00, 26.65it/s]\n",
      "Epoch: 13 Loss: 0.0207 Acc: 0.9676 : 100%|██████████| 1000/1000 [01:52<00:00,  8.80it/s]\n",
      "Test : 13 Loss: 0.0269 Acc: 0.9647 : 100%|██████████| 100/100 [00:05<00:00, 23.93it/s]\n",
      "Epoch: 14 Loss: 0.0195 Acc: 0.9734 : 100%|██████████| 1000/1000 [01:52<00:00,  8.98it/s]\n",
      "Test : 14 Loss: 0.0163 Acc: 0.9767 : 100%|██████████| 100/100 [00:05<00:00, 17.39it/s]\n",
      "Epoch: 15 Loss: 0.0181 Acc: 0.9751 : 100%|██████████| 1000/1000 [01:52<00:00,  9.02it/s]\n",
      "Test : 15 Loss: 0.0242 Acc: 0.9669 : 100%|██████████| 100/100 [00:05<00:00, 17.34it/s]\n",
      "Epoch: 16 Loss: 0.0126 Acc: 0.9840 : 100%|██████████| 1000/1000 [01:52<00:00,  9.02it/s]\n",
      "Test : 16 Loss: 0.0298 Acc: 0.9570 : 100%|██████████| 100/100 [00:05<00:00, 24.91it/s]\n",
      "Epoch: 17 Loss: 0.0120 Acc: 0.9833 : 100%|██████████| 1000/1000 [01:52<00:00,  8.85it/s]\n",
      "Test : 17 Loss: 0.0185 Acc: 0.9722 : 100%|██████████| 100/100 [00:05<00:00, 17.38it/s]\n",
      "Epoch: 18 Loss: 0.0139 Acc: 0.9814 : 100%|██████████| 1000/1000 [01:52<00:00,  8.89it/s]\n",
      "Test : 18 Loss: 0.0138 Acc: 0.9809 : 100%|██████████| 100/100 [00:05<00:00, 25.59it/s]\n",
      "Epoch: 19 Loss: 0.0138 Acc: 0.9779 : 100%|██████████| 1000/1000 [01:52<00:00,  9.01it/s]\n",
      "Test : 19 Loss: 0.3607 Acc: 0.7903 : 100%|██████████| 100/100 [00:05<00:00, 17.49it/s]\n",
      "Epoch: 20 Loss: 0.0128 Acc: 0.9799 : 100%|██████████| 1000/1000 [01:52<00:00,  8.87it/s]\n",
      "Test : 20 Loss: 0.2395 Acc: 0.8163 : 100%|██████████| 100/100 [00:05<00:00, 25.22it/s]\n",
      "Epoch: 21 Loss: 0.0092 Acc: 0.9887 : 100%|██████████| 1000/1000 [01:52<00:00,  8.93it/s]\n",
      "Test : 21 Loss: 0.0358 Acc: 0.9598 : 100%|██████████| 100/100 [00:05<00:00, 17.50it/s]\n",
      "Epoch: 22 Loss: 0.0116 Acc: 0.9841 : 100%|██████████| 1000/1000 [01:52<00:00,  8.92it/s]\n",
      "Test : 22 Loss: 0.4531 Acc: 0.5920 : 100%|██████████| 100/100 [00:05<00:00, 17.23it/s]\n",
      "Epoch: 23 Loss: 0.0116 Acc: 0.9846 : 100%|██████████| 1000/1000 [01:52<00:00,  8.96it/s]\n",
      "Test : 23 Loss: 0.0089 Acc: 0.9878 : 100%|██████████| 100/100 [00:05<00:00, 17.51it/s]\n",
      "Epoch: 24 Loss: 0.0079 Acc: 0.9884 : 100%|██████████| 1000/1000 [01:52<00:00,  8.94it/s]\n",
      "Test : 24 Loss: 0.0093 Acc: 0.9871 : 100%|██████████| 100/100 [00:05<00:00, 17.42it/s]\n",
      "Epoch: 25 Loss: 0.0078 Acc: 0.9904 : 100%|██████████| 1000/1000 [01:52<00:00,  8.74it/s]\n",
      "Test : 25 Loss: 0.0154 Acc: 0.9775 : 100%|██████████| 100/100 [00:05<00:00, 17.39it/s]\n",
      "Epoch: 26 Loss: 0.0086 Acc: 0.9896 : 100%|██████████| 1000/1000 [01:52<00:00,  8.98it/s]\n",
      "Test : 26 Loss: 0.0803 Acc: 0.9563 : 100%|██████████| 100/100 [00:05<00:00, 17.20it/s]\n",
      "Epoch: 27 Loss: 0.0104 Acc: 0.9862 : 100%|██████████| 1000/1000 [01:52<00:00,  8.87it/s]\n",
      "Test : 27 Loss: 0.0557 Acc: 0.9373 : 100%|██████████| 100/100 [00:05<00:00, 17.57it/s]\n",
      "Epoch: 28 Loss: 0.0077 Acc: 0.9910 : 100%|██████████| 1000/1000 [01:52<00:00,  8.97it/s]\n",
      "Test : 28 Loss: 0.0081 Acc: 0.9905 : 100%|██████████| 100/100 [00:05<00:00, 26.49it/s]\n",
      "Epoch: 29 Loss: 0.0079 Acc: 0.9912 : 100%|██████████| 1000/1000 [01:52<00:00,  9.20it/s]\n",
      "Test : 29 Loss: 0.0717 Acc: 0.9101 : 100%|██████████| 100/100 [00:05<00:00, 17.47it/s]\n",
      "Epoch: 30 Loss: 0.0076 Acc: 0.9894 : 100%|██████████| 1000/1000 [01:52<00:00,  9.02it/s]\n",
      "Test : 30 Loss: 0.0114 Acc: 0.9846 : 100%|██████████| 100/100 [00:05<00:00, 17.36it/s]\n"
     ]
    }
   ],
   "source": [
    "optimizer = torch.optim.Adam(model.parameters(), 1e-3, amsgrad=True)\n",
    "epochs = 30\n",
    "for epoch in range(1, epochs + 1):\n",
    "    train(model, optimizer, epoch, train_loader)\n",
    "    valid(model, optimizer, epoch, valid_loader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T12:48:21.757260Z",
     "start_time": "2019-06-18T12:18:50.676872Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Epoch: 1 Loss: 0.0045 Acc: 0.9938 : 100%|██████████| 1000/1000 [01:52<00:00,  8.96it/s]\n",
      "Test : 1 Loss: 0.0044 Acc: 0.9944 : 100%|██████████| 100/100 [00:05<00:00, 17.20it/s]\n",
      "Epoch: 2 Loss: 0.0039 Acc: 0.9935 : 100%|██████████| 1000/1000 [01:52<00:00,  8.94it/s]\n",
      "Test : 2 Loss: 0.0040 Acc: 0.9943 : 100%|██████████| 100/100 [00:05<00:00, 17.47it/s]\n",
      "Epoch: 3 Loss: 0.0047 Acc: 0.9948 : 100%|██████████| 1000/1000 [01:52<00:00,  9.09it/s]\n",
      "Test : 3 Loss: 0.0043 Acc: 0.9941 : 100%|██████████| 100/100 [00:05<00:00, 17.12it/s]\n",
      "Epoch: 4 Loss: 0.0049 Acc: 0.9935 : 100%|██████████| 1000/1000 [01:52<00:00,  9.08it/s]\n",
      "Test : 4 Loss: 0.0050 Acc: 0.9941 : 100%|██████████| 100/100 [00:05<00:00, 17.33it/s]\n",
      "Epoch: 5 Loss: 0.0033 Acc: 0.9951 : 100%|██████████| 1000/1000 [01:52<00:00,  9.11it/s]\n",
      "Test : 5 Loss: 0.0047 Acc: 0.9937 : 100%|██████████| 100/100 [00:05<00:00, 26.28it/s]\n",
      "Epoch: 6 Loss: 0.0029 Acc: 0.9959 : 100%|██████████| 1000/1000 [01:52<00:00,  8.84it/s]\n",
      "Test : 6 Loss: 0.0037 Acc: 0.9960 : 100%|██████████| 100/100 [00:05<00:00, 25.36it/s]\n",
      "Epoch: 7 Loss: 0.0030 Acc: 0.9969 : 100%|██████████| 1000/1000 [01:52<00:00,  8.91it/s]\n",
      "Test : 7 Loss: 0.0039 Acc: 0.9953 : 100%|██████████| 100/100 [00:05<00:00, 26.32it/s]\n",
      "Epoch: 8 Loss: 0.0049 Acc: 0.9938 : 100%|██████████| 1000/1000 [01:52<00:00,  8.91it/s]\n",
      "Test : 8 Loss: 0.0036 Acc: 0.9949 : 100%|██████████| 100/100 [00:05<00:00, 26.22it/s]\n",
      "Epoch: 9 Loss: 0.0026 Acc: 0.9967 : 100%|██████████| 1000/1000 [01:52<00:00,  8.84it/s]\n",
      "Test : 9 Loss: 0.0041 Acc: 0.9948 : 100%|██████████| 100/100 [00:05<00:00, 17.29it/s]\n",
      "Epoch: 10 Loss: 0.0025 Acc: 0.9975 : 100%|██████████| 1000/1000 [01:52<00:00,  8.86it/s]\n",
      "Test : 10 Loss: 0.0026 Acc: 0.9963 : 100%|██████████| 100/100 [00:05<00:00, 17.10it/s]\n",
      "Epoch: 11 Loss: 0.0053 Acc: 0.9942 : 100%|██████████| 1000/1000 [01:52<00:00,  8.96it/s]\n",
      "Test : 11 Loss: 0.0030 Acc: 0.9959 : 100%|██████████| 100/100 [00:05<00:00, 24.42it/s]\n",
      "Epoch: 12 Loss: 0.0021 Acc: 0.9974 : 100%|██████████| 1000/1000 [01:52<00:00,  8.66it/s]\n",
      "Test : 12 Loss: 0.0028 Acc: 0.9964 : 100%|██████████| 100/100 [00:05<00:00, 26.51it/s]\n",
      "Epoch: 13 Loss: 0.0027 Acc: 0.9960 : 100%|██████████| 1000/1000 [01:52<00:00,  8.95it/s]\n",
      "Test : 13 Loss: 0.0037 Acc: 0.9946 : 100%|██████████| 100/100 [00:05<00:00, 24.82it/s]\n",
      "Epoch: 14 Loss: 0.0073 Acc: 0.9905 : 100%|██████████| 1000/1000 [01:52<00:00,  8.89it/s]\n",
      "Test : 14 Loss: 0.0042 Acc: 0.9945 : 100%|██████████| 100/100 [00:05<00:00, 17.40it/s]\n",
      "Epoch: 15 Loss: 0.0019 Acc: 0.9971 : 100%|██████████| 1000/1000 [01:52<00:00,  8.99it/s]\n",
      "Test : 15 Loss: 0.0034 Acc: 0.9957 : 100%|██████████| 100/100 [00:05<00:00, 17.38it/s]\n"
     ]
    }
   ],
   "source": [
    "optimizer = torch.optim.Adam(model.parameters(), 1e-4, amsgrad=True)\n",
    "epochs = 15\n",
    "for epoch in range(1, epochs + 1):\n",
    "    train(model, optimizer, epoch, train_loader)\n",
    "    valid(model, optimizer, epoch, valid_loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 测试模型输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T12:48:58.168479Z",
     "start_time": "2019-06-18T12:48:57.536996Z"
    },
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true: DYE8\n",
      "pred: DYE8\n",
      "true: BMRV\n",
      "pred: BMRV\n",
      "true: 9NPM\n",
      "pred: 9NPM\n",
      "true: CCVM\n",
      "pred: CCVM\n",
      "true: QN7Z\n",
      "pred: QN7Z\n",
      "true: PGK1\n",
      "pred: PGK1\n",
      "true: 4SIU\n",
      "pred: 4SIU\n",
      "true: A662\n",
      "pred: A662\n",
      "true: KLUM\n",
      "pred: KLUM\n",
      "true: NOFK\n",
      "pred: NOFK\n",
      "true: MAIR\n",
      "pred: MAIR\n",
      "true: BOXU\n",
      "pred: BOXU\n",
      "true: OA18\n",
      "pred: OA18\n",
      "true: FQEK\n",
      "pred: FQEK\n",
      "true: UIED\n",
      "pred: UIED\n",
      "true: Y4MR\n",
      "pred: Y4MR\n",
      "true: SZXQ\n",
      "pred: SZXQ\n",
      "true: 5OND\n",
      "pred: 5OND\n",
      "true: 3HEP\n",
      "pred: 3HEP\n",
      "true: IKJ8\n",
      "pred: IKJ8\n",
      "true: LTWA\n",
      "pred: LTWA\n",
      "true: K5O7\n",
      "pred: K5O7\n",
      "true: 4R71\n",
      "pred: 4R71\n",
      "true: JL3Z\n",
      "pred: JL3Z\n",
      "true: ER9Z\n",
      "pred: ER9Z\n",
      "true: EZ1S\n",
      "pred: EZ1S\n",
      "true: EGKF\n",
      "pred: EGKF\n",
      "true: XF0X\n",
      "pred: XF0X\n",
      "true: Z8P4\n",
      "pred: Z8P4\n",
      "true: ADCK\n",
      "pred: ADCK\n",
      "true: B1K0\n",
      "pred: B1K0\n",
      "true: D8KG\n",
      "pred: D8KG\n",
      "true: XPTH\n",
      "pred: XPTH\n",
      "true: T1ZY\n",
      "pred: T1ZY\n",
      "true: 8WG5\n",
      "pred: 8WG5\n",
      "true: P7RV\n",
      "pred: P7RV\n",
      "true: 0HLH\n",
      "pred: 0HLH\n",
      "true: U0AG\n",
      "pred: U0AG\n",
      "true: 56PK\n",
      "pred: 56PK\n",
      "true: 6IJG\n",
      "pred: 6IJG\n",
      "true: 2FN2\n",
      "pred: 2FN2\n",
      "true: 7QNI\n",
      "pred: 7QNI\n",
      "true: OKZH\n",
      "pred: OKZH\n",
      "true: 1DI8\n",
      "pred: 1DI8\n",
      "true: 62T2\n",
      "pred: 62T2\n",
      "true: 85ET\n",
      "pred: 85ET\n",
      "true: PDBO\n",
      "pred: PDBO\n",
      "true: 0MJD\n",
      "pred: 0MJD\n",
      "true: U9YB\n",
      "pred: U9YB\n",
      "true: 6ZOK\n",
      "pred: 6ZOK\n",
      "true: B5PR\n",
      "pred: B5PR\n",
      "true: A3MI\n",
      "pred: A3MI\n",
      "true: X39Z\n",
      "pred: X39Z\n",
      "true: SVRY\n",
      "pred: SVRY\n",
      "true: 96L9\n",
      "pred: 96L9\n",
      "true: 2EL3\n",
      "pred: 2EL3\n",
      "true: VT0O\n",
      "pred: VT0O\n",
      "true: QWC5\n",
      "pred: QWC5\n",
      "true: OP3I\n",
      "pred: OP3I\n",
      "true: 570W\n",
      "pred: 570W\n",
      "true: OR0F\n",
      "pred: OR0F\n",
      "true: X65U\n",
      "pred: X65U\n",
      "true: 7W02\n",
      "pred: 7W02\n",
      "true: QK4Y\n",
      "pred: QK4Y\n",
      "true: SU5B\n",
      "pred: SU5B\n",
      "true: 1WK1\n",
      "pred: 1WK1\n",
      "true: M1K0\n",
      "pred: M1K0\n",
      "true: NYVL\n",
      "pred: NYVL\n",
      "true: ZQTO\n",
      "pred: ZQTO\n",
      "true: IL3Z\n",
      "pred: IL3Z\n",
      "true: VGEL\n",
      "pred: VGEL\n",
      "true: 89NK\n",
      "pred: 89NK\n",
      "true: EFW8\n",
      "pred: EFW8\n",
      "true: RR68\n",
      "pred: RR68\n",
      "true: PKIS\n",
      "pred: PKIS\n",
      "true: 5OA9\n",
      "pred: 5OA9\n",
      "true: SWTO\n",
      "pred: SWTO\n",
      "true: F4GT\n",
      "pred: F4GT\n",
      "true: MMHS\n",
      "pred: MMHS\n",
      "true: 5FGG\n",
      "pred: 5FGG\n",
      "true: VKNL\n",
      "pred: VKNL\n",
      "true: F84U\n",
      "pred: F84U\n",
      "true: EK0H\n",
      "pred: EK0H\n",
      "true: 1LNW\n",
      "pred: 1LNW\n",
      "true: GIYU\n",
      "pred: GIYU\n",
      "true: UHEI\n",
      "pred: UHEI\n",
      "true: V7XJ\n",
      "pred: V7XJ\n",
      "true: SWA9\n",
      "pred: SWA9\n",
      "true: S7AL\n",
      "pred: S7AL\n",
      "true: UKV3\n",
      "pred: UKV3\n",
      "true: 5NON\n",
      "pred: 5NON\n",
      "true: 2QF3\n",
      "pred: 2QF3\n",
      "true: 5891\n",
      "pred: 5891\n",
      "true: R7SM\n",
      "pred: R7SM\n",
      "true: U0AD\n",
      "pred: UOAD\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAABACAIAAADDDu+IAAAchUlEQVR4nO19W6x113XWmGNe1tr3fX67LXViu79cHhAPoKKqDzyYOEldXMd23DapQa1JAEGJGi55bHiiDwipiBSlAVWkiYLkVAW3prUj49iWeagqRRTlAYEUO79/39LW9jn7vi5zzjF4GGuts84+++yzz+2vXTJs/dp7n3WZc64xx+Ubl6WWcw/fp/cw/dUf/hv/+3v/8z17a9UwkC/95GDqvQcARDTWdHudNE0RUQ6Y7E9uvHLzySeeevSxh6/fc/f42vgqB///O8lq/9q/+gIAXP/RH/nc5z97yxb8TLfG5lOM0XufZ3me5VmWFUURQ2TmtYveePnVJ5946oqn8H0CAGjW+dHHHn7P3ho3/8wADER8vot+ny6FHn3s4es/+iOf+5f/9Bzynmu66lsfqrA8L/bf2c+zHACUUi5x/WG/3+9preH9qb8m+xP58L4Y7XHaZfwxElEkIgUKlFDzp2itbSyQS7+10BEGmrx7kOcFESmlrLODYb/f72ujN170pHsQUYwEAFrj+UZ/KfR+5PhzkPd+OpnFEBEVam2MRo3AEEIwxjjnjDEK1dU9CNN8UgAi7+azOTBoa3q9LjEho3B1+xkceTxH/xRjnE/nMVJv0EOltNZKKdQIALeMn9pm4JNPPPW5z3/2cq9PRKIdUKFCderxV0ExRoqU53mR5yFERESNxmhELPISAIzRuSmsMZ1exxiDiIfS6fLIrH2fTmdvvPbWc08//1MP/WS310k6iaiw9r23Px4i8t6HEIt3CkRExP6gT0TdXudWCqRzWGynym15ZjFGZi5Lb4zudrsXHehpREQxxhgJjho0RDSbzr33RMTEkSMRxRCZKusn+KC198Z475M06XQ7Wustj+B8Gr8lgZRazBdvvf7Wb33pawDw3NPP/+W/co9wg7V2jXm3Px6lMMZSnDittS/Lbq9LaXKMXa+QHn3s4UP9tTeSVWZmhUqpDSJ9i0xtiCJVSAezTVyaDm7BRCjSbDovi2LNIG64ufreaJCaREYS+xhjCDGE0O31RESdb/ob6fBCSilr7TefeVE45SMP3Ed0oiW/xUpXoKDFbDHGEGMIgZh2HNPFaXxtfP2euz/3+c/K8BggeL9cLrMsy7P8+Ix2ASlkLxV5XuSFDyHGyMBw9eqLiEIIeV4IwtL870vfcI8Yz5WSag2JmSlSCKEsyyIr5rP5fL5oozNCF8FoDmUCakSt//Yj9z/95Dc+8sB9H7jrjsGwL3dau9/42vg6gGiunViVgZnhnB7lOUkGJqsfQyzLsigKWWVttAKlENtSdbtMlSexXK5ItjVR8CHPcq31dr1wCaRAAShQzQoeaoN6sypUxhhmFrEkJxxufgYCCiEScAxBI/Z05Vw3dG6MpsVACn/wL91eFPnf/8zjMdJwOJBVE29w7bRt0GRNEar9gRoRdePN3TKiSN572XMxBGJgJmN0jNTrdV2SaH1oVx5ReZtm50NQSlXmM0MIYbXKGGAw6F+1bde2Hyr9W/nsoBSKt6sQmQgRI0YAJuLG0gcAYIgUOTAjhxAp0hoDnTr9E8fWjoWVZTmdzoqsCD4AgEtdp5s659I0XbvfFiKiIi8O9idFXsgEXOL2ro3TzhkuckEiIuGe6WRWlmWMsZF/SoHWOumkSeK6vW4jP7abkGI1Tw+mq+WKiEC0Bqq0k4zGoyRJro6HytJPJ9PlYkmRAAARtTVao0YEAIWoNVpnAVSMMYZonY0hlEXpfYghymgBQHgOEZ1zo/Ew7aTtMZ8bM1szaxUiIioRgIrX7bJdSPxJ2SVyeiOTznyt8xIRLRaLbJV770MIMgzZx0TMHDnLZWX7g76csn3hmJliLMuSmWVezAwEwYciL5xzVzcXVauw+jsYjb1+T2us2FhXC0tRRxOdc4jdGGO2yrJVJhpcpCYzR46+LJeLlbGm7RudGyc7wkBKASqlEBUAM9BG+3kX4ta/AEy8FhW5UhK7pyhK732MUewEpQBksQgYmCIFH7zxwYe2U7YFHRUZwMByQbEwDvYnZeG9D7fdfhteGSC0BjWh1tZaa418bn6XxyXTMUYbo5PEzWbzkkuBHgAAGGKkoihWi1Vv0Lu4Abd2ssJqGWTEDGL8nuXpU6RIFEI4/KXWyBcZ6BkGQLRarionhUGh0kZbZ51zzjnrLCoUmKTIisVi2XgllTPyq79+45WbDSc112SAQ+WllEI1n81ff/WNL/3b3/zO/3l5cjDZNJZLICUy6ChzokZtjDYGlWoEfNuiV0oZY1zihqNhkjqBc+VcZo4hZlm+XK4u/lCOMJCwjhLtA8AMfHb3KVJczhdt4cXMofTnFmdnIlmdIi+998I91pokcWmnMxoPR+Nht9exzoo5XHqfZ/lysQwh7L+zv82VZWCiSikrMNYsF6s3X3vry7/xtZs3Xv+D//qNQ1PjskmhwqNBocp6rq3oLecaY6w1aSc1RrctcRHSsscu+FyOQXuqZnnZdsQg/+1MFClGai8oE0ciJgK4FUZ0CEFGrpTSWlvr+oOec04gROccgCIi9gwMwfssy5g5Em1xZRk4EjNVOsIYrTU+98yL8sf7H/ro1YEUiNjpdFbLLOIRi3hHs1drbawx1oQYiakZZ4wxxujL0lpzkRDHJv2nROlWZtc5OHRtQEopYL41MSMiIiJmAgViCrjEusQZa0S8S6KctUZsTyIuC59luS/9w5948MQcBoYmkoCI2hitzU8++OEP3nXHp37pFz949x3D8VWh0kopbXSv30UBHRiYeP+dg+9+58ZGbXv89CSp4hiNIhS9Um30i8nODcGF+jaKgYlrOIF5u7Rsn7+usBG7vS6qW+GFMbP3nkT8GN3pdnr93pr8N8aknRQAiryUSJPykHbSO+/+4L/4lV9WSq1xDzMTk4BGzKyUStPkB37odu/9p37pF0Cpbrcrq3QV0UqoWdYYI+bawbsHf/anb//Hf/eflFKnhopFDBtjGtCrAoeBiTiEcNkqDED2LtQ3EXTBGLP72rSPlAepEBtv86pJ8BKJzHQ6HWPWRbQwtOC2ZUEAECOFEEfjYbfX3cgEbUGsAJhZwC3BusQet8ZeHVjaHlPp/e/99u9TJG30ScBxW8EpUBJmQI0CDlcSiMj7cEE7+qgRjZI6Upnxsm5EFOkMppZG+a915cokvxVGtALlkkRsTGM16g05DIhojLHOam1EscqyCvy48bIaNRE1tiAiUoyNaxNjzPM8xIvu5i3EjeRgZqKP/vSH77x+5z//lV/eCByvu5MVhKgqH62VcUa0HuQ/Kx1hIFTY6aTiDIJAQTXtvjSosdfrqTYD3cIoWLUDKr45UWiKZSD+rRwVKWar/KStQkyIqpqIUqi1S5w21WZj5hBig7xfDYkVxgAwHA2H48Fjf+/nxnvjE7nnqDspShA1NuGzGha66IjXJZDWutNJlarWRfblme7SYBKHP4kJdUtInFu5OxOdxLtyTKfTsc5q1FCn3VRY8/HjQcm1BWuxpiJttMiwKrH0ypz5tmk5m85mk/kTX/mdycFkowV93J2UCAZqPBqsBwDFZ3y+a7RulyhUCgWLBgaOkWKks2KJAIc+CzNfaIBnJ2N07UJuGzUiWmf7/V6FkTDUDsMmBqqQDUbEwbAvEq7T7VhrRIZRpHhhe2ILqRr+mU3nb7z21pe/+NU3br7xe7/9+xsPPp5sU0MPWq2xEJ9ZQKzRCV6Y5GaTrOrZUGSqbIkje5GvcGtuGIBSWIOg2wauEZM0cYkLkpwlQXuitexPMQSrWKZGpZRGDQqMMWm3EyMJPuR9iDEymasCLOqrPvf08/LhkU9+7PhRG5NtqnyhtXShunjjIoPa4oXVd6q8011vQ5GWi9VartOtz+UA5gpR3OJdK6W17g36RVFK2miRl86VbeAfamhAQGjVyuxGxE4n9UUpJXUhhGy1QlTW2Mt1OevYSTWkjz7w4W9+44WHP/HgXdfv3IgibvyxCjOscZBSF5SZmyVQ6z5VBG49GHMyRSI6aooao5W6kozu46RADCBmgBDC9978k8FwYIw+Ca6V1AFjjC89M5dlWZblenoGN/9UEVkJqSqlUGHaSVerTOzFPCuAYTAaXDpmobWWAMFwNFB33/Hpf/J4p9cZjYa7X2E6nS3mizzPO2nn8FeGC/o4J85TeIgrS+JMEbEjMlH45tYVLihQqIw1CtRkf/Kd//vyr/3qF7bDtQKQNBhJWZRrtnATdhK1eJiGDKBQee8VKIFVYogi9i55TtUAKhIvbDjs736Fyf7k5ndf+/pX/stitmg0rCACF9RimxmoEplQ7bYz3uC4lj1bNO3ipBTOZ/PXX33zN3/9t25+97Xteb5aY7/fq3wxphiPGZUKUOsmK21NulhrmhkTRcHuL3My9RjaeuF4fP4kmuxPbrz86h9/69tf+dLX5rN5t9ttC1dmPnh3crC/2ZvbhU6skzhkeD6kNTU02Z8c7E8AYO/aISAhVsJa7JdvYT5QBYSieu6Z2tj8xIPbj9daG6u9VzXMf4SI2Je+Mafa4gCVMtYaY4IP4sUzkdoatDlf7p9qZUkc/rb1Lgf7k8nBFABeePalyf5kfG386GMPX7v9Wnt7TA5m77797nPPvPCzf/fjZyrGaOj0QhvJg5FoxvG/vvjsSwcH0/vuvxdeuSm/hBBQqSRNev1efYnN/HdFpJTqdFJE/OgD9z33zAsP/dxPn2Rstk+pUhCq+PER05uIiqKo5MqaQlZKa+0SV3GYAHMnT7apnvnQ/fdev+fu9saD7bzVFF1UjQu4bQ+32aWhF559ST7cd/+9ADDeG+1dG3e6nf139uX32XT+1hvf+8qXvmadPXf55SYjGhW2imGZOdZw4vFF+dD9904Ops1YASCGGLz/sZ/461UtnFLW2tF4aJ3duzZeW7KrIERUqEbj0Qfu+sCn/vEv2MRJZeOW1DuFyhiNCiNEJioLnyTJYQaW1JRRY0sfkVCImCQuW2UqiJFE3nuXuONr1WDEk4Pp//rWt6/fc/enP/P49Vap+JbKLKVAI85ni/13DuazGSImaToY9ptd3X4EQm2+aa6WZ3l79P/9D74pWS7nbpixgYFQYZIm2Sqr8qckbrvJDBpfG4+vjSf7k0d//qGG/Zl4tVq98OxLwQdZa2utdXa5WAHAh+6/d+8EBlqb6sVIAcBwNAAAbfRysdreZkAir1pjtVGYGoar0CRmpWrvgAHqkC00MqspoKlp4+2efOKpycH0xsuvNl9l37d568bLr376M4+vrRLFuFpl33vzT/7wpT8CgNF4ZJ211hpbPUFhFwBojIq1MFk1qzqpUmZ0/4MfefG5//HJX/zZc7cPOEECtbzuKn/8ZLxyfPSpM/Pbf/q2S9w7f/YOMyilrLPjvdF8tnjx2ZdefPalLQNtq8IdaSPboVJS7NKUZ8QYm7U+ThJ+Mdb4EJiPFMQQUVmUbUdCKgx96VfLrNvrMMB8tmhS+yhykRdS8zCbzNqa5WB/cv2euxtRIV//+Fvf3rs2PtiffPmLX73xyk2Z0fFVokjel+++vQ8AP/E3f3y0N3KJ6w96xhhUau+2vb16MwOAiLG1OQqa5UNoJONwNDTG/KN/9g9G4+G5963a2OKuLMvJ/mS1zKRTh0vdcDiQIphTr0hEeV4cvHsgyAqiGo6H/X5/uVge19NrdFwO70LN5msohLBarpaLlSDg1tnx3ti69eq2hpg5eD+dzn3pASBJ3HA8FO0QY1zMl0VexBCY2SZuMOwXeenrJF2loF27rpQyziZp0u/3EHFtRpP9yZrn0egv+X3v2vjjjz18XEgTUZZli9my2+sOR4Px3ijtpGmauMSlaYKtFjwndRaTlIGiKLNlVhYlMytUzrn+oNfr9Tburl3s/fXT5Jyy9Gvh9N2dqBhpOV+0arYVEYNaF1Qbh9tWhbvTcbZjIu+DL30IAYARJane4skbIHjvS++9R0TRuXKwmHSl94ehjHqaa/esPyiJsllrtdHHmXs7naTHpVJgcjAtixKqAcQQAmokclhPa1tWbp0y0IxcQZ2Qvwn23LFa3mw852M/88De7WNnXXPv3YNZVGdKCtcx8fFi7I10KodtpJPYLoQwn82LrPKerLOjvdHxLhENxRBns3mRF1qjWBiqqiOj1SpbLVZxe/Jec1kGharX73Z73dt/4LbLdBpqR6wxy0KMGGJ7d28pMGUBIyIxH1pvCpV12+x9OK0/jtl8ztef+oef/VQrGlZVaOw6UwBEZGJpqHCWU89MJ7Fd8GE+n89nVS+BJE3EiT2Jgbz3i/liPltorcd7oyY1MYS4Wq1mk5k/VlhSA9QV2o6I4rEqgLSTjPfGLnF4ecjF2oUEE29Hjbb0LGgs6GpX19pWgYRYNwxyx2r5IxKoOedn/s4jWmtUqtZD5wKj60TE3QXYmUC27QejRmutcy6nnCNT1WUnrgVK21QjyFWmVSVUmIM/InsaphFl5xIbI2lE1BhCzLM8+BB8WCwWfehfLg8d0gng/knrJlCWxDQrPEJJtvGJnumO1fJHGKg5567rdzpnp5MpNBn8tYOrlDrtMdfB+3qCTBW6th1IPFOLmlMPVkolaeJD8N5LWu5ysWTmTndDljS08PYmbi2/E1ddm5rLStKjMdpY41ySdlJEFUM01vjS+9IHH0KIRV5qzKQ12JaJnJ+47tuxy7EkEqhtSyisq9CPH797A5bDk9s9dfaujRv5DHUdWlkURLSlfLO6IqLE3oXjpNhen9Zf7UwtanY5WLIHE+ecs4iaiIqiXK2yotiQeCqsEwNJ1ky7gqBaBgkJV1XDxlpjnXOJSxLnnLXWit/ezJqZQwjSPmz7XM5Gx5aw+UH8uJNCWgxMRCHExh6VJhNrLRbaNG45iVvoyMntc47EU5mDD2Xp9985OPXJIapuryPV4wCAGvvDwS75QGdqUbPLwUoplzgpiQKpeAxRela0Y3OyPfIsjzEqpVySVLkT9WWsc1BbOUkn6fV7vX6/0+30uj1BnBsqiuLw9k1K22XZf8ciqFWcemtRNtTbgyK1E5SVUtYac9m18fVQj4YMD00w3la+WV2xlozyVdd06lAEpP70Zx7fBRXdpZGxCCEtjUsBACCEUOTFYrH0raQLJl4tV6tV5r1HiaMdcWs5hiAYjzGm2+30B71ev9vtdqRYsS1ZJQDSVHoAQPDx8oTQEZVVdS1gODhNHsv+DyESH3KP1hpbK3Nu2gzOKgBZGoUKCKAqWI5qB9uqsiNq/qsDA9tCUWelM7VIs8Zaa4I0pSMui5IiAfBgOBCo2odQFmXwgSIpa/HYpoy184Iaq8a5m9QxImqjrTVSScfAZemzLHOJ1Rd+To0mbVqVSdkVAACfIo+JuBQHvrHkUBlrup1LaHt6Arpfm4qIKJUuzKxAjfdGzrntT46IfVm2sqh5uVxpY4w5sc2vSOAvf/GrAPDisy/92I//tVPHvSO4opQyxvQH/UhUlmWsjQAbrCis4MN8Oi9LL18R130mpZRzNltlIELlSGrXOmnUnW6XiAsqIkVpTliVZV7YF9PatKt7xUUUHtq+q2UH+3AYckClksSZ87Yhb9OWfCBlnS3LIqrGsWXY4ckxUVn6SBEAVN1HJ8+yppXTRrq69yigRuusSxwRcayKb3zpy6L0hV+uVgJYMzFq1GbdyVd1RmVty2yzaFBj2klCCGVRig0kkbiNwfkz0ZpRIeMQk2jv2jhJTtzVogAExGr0F2rtnLu4XIQtDCQC2VgbQuSmRDXGU5WRxC/r5ZZCKkw7nS2nwAV69O1CiDjo9xUARSL2wOC9n03niMqXIVIEBq3RWHs8aE/EMUZi3tyo5ygdPmYxVi4dPj3qtitVZZltWTGKJP7BIfyDyjnbQO0XpG0M1OmkslOlM1cIIVtlEnU7iYfEVPJ15AgAFKL0p9nCdmdu+3pGQkRlVa/fi5EUKBmepxIAmEEKsW3ihsPBpqwPDiG2m+SdQgwxBERsjJXL6gzZ7nXHDZp8MkMzMxNLDyR5i5ccb611iTP6ErQqbFdhWutONy3y6r1PMcbS+/ls3uv3rd2Ajwn4MZ/ND8EGVACwSx+1q84yOzSG5PUk0uWZQYY33hslSYJ1TXebmFhiFLBbHbBCZRNXlj5g4FgBQpcliloVZw0QvfnSojHyvMhWq7LwTcMJeR1C5zLMZ6FtKa2IaLSxzlatrIiLLA8+EPFoPFwbATPHEBfLZcsAqoO9t6Sxy6mEiNaYbrcjtnOTWpWmSZImx1sZN+RLv0upa3OXNEnKoihLkXAs/jOeXACzI61bQVvFBzOXZZlnWZGXMQZBdLXW2mqXJE1F9sXplJxorXWZF4vFMpR+MB7EyEQeFKRFKqEJbKromYs891ITw3WoRetut3PLGrucSqix0+0aa/IsJ2aK5JxtkMaNJJl7VMdhTl11pZQ22hjjnI1a2vpcjrKQ9VSIIA24JTGyjsC0b0FEwYeiKCVBRZqjgwJttHOu1+9elviBUxloOp298dqbv/Off/e+n/pbH1AwGA5E0kwPJkmSGGsF+C/Lsiy91ihNrKGuItBGn/uVVVdBSiljNGJirZUXWzlnt4SrqjLtSPJwdnRblFLdXhdAucRVfS8vQwZPJ7Miy6eTWbfbadrHSSGbMUZrXYetKYQ4m87Koqwwd66YzyVuOBpcbnjulAKUV1+5+e//zX9487W3nv/Gi1gD/DHEsvTZKsvzPM+L+Xy+WmVlUS4Xq8pYUwAKtEZ7DKt9L5AI804n7Xa7zm5zsJk5yzIiqnuh7Hp9Y8xwNEjTJEmqN3ZdcMxSGfiFf/0br7/6xmw6B6hCJTHGsvR1rzrK8zxbZfPZvCzKyvAA6RWJruokccnB3VOu9btf/28AgBoffewREcXVcjMQcZEX2SrLV0UogyRLiAPfxBC63e57R3+1SVWN3zZ3oGpI7CTJwOKzVAxiTZcy2iZ4fPPGa889/XyV7d9qYkn1+27LoizyosiL4EMNWCuBY5xzZpPrc0E6RYUJPPPxn3/ojjvvMEZnqzyGoJQS3m/a3jY1rACV9WOM7nY71r2H9Nf5iQEUaNz8oqRLoVMToQRoRcRHH3t4TZOy9H5gWC5XRDGGGCMRs3S2M9Y4Z5M0ObfntX1sm5Pq105mgNF4WBTF5GDqSw8MAr4dsd1qZkKF2uhur9Pr990loVV/XlSWfnowyVZZiNFae9vt15I0ufQ3fpz6ds7mgEc++bEf+uEfRMQiLwBA1y//UqgkDfwQflNKInf9Yd9ZK0ee41mcOrZTGKhNIYTlYlkUZfChiReK9GkGJ0kCSZoYY7Z7N+8LKks/OZhkqyyGmHbSa7ftSXvXS6QdX9IuO5mIkjSZTmbCQIgICgRirlI7qmCdss5Y5zrdNE3TLQjFxcd2hncIImK/309SX7X/4aqlN0VSdQMsY02SJNK/8kpSOW81sdYauMJCr2g/7BIHbF5/JmFdaNlka+m2qKsWommaWGsvOOZTx3Y2BgIEhUqPjOQ4R6LlfBljlOaKvX5XlttcrPn5e4eUUk3P8uEVdP0ROmsc8LAA5KhbWDWlN1obU9UcXvhFu6eO7Qwq7DgRHb7VoHrtWT3cvxgMVJblYr7Is9w6Nx6PtpQmXoR2LyUgoizLZ9NZkeXttoNaa9TonB0MB9poRH1Z71w/dWwXYqC/8CRmnySRob4qFbY7MbO8qakojnSTFQFprb31g/w+A22jBvh5j4ARzEzSA+toqwKRQH8u/H0LX8T9PqT3CN80pJTSWl1KIthl0f8DvdBwWhoDNSUAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=192x64 at 0x7F203C928550>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.eval()\n",
    "do = True\n",
    "while do or decode_target(target) == decode(output_argmax[0]):\n",
    "    do = False\n",
    "    image, target, input_length, label_length = dataset[0]\n",
    "    print('true:', decode_target(target))\n",
    "\n",
    "    output = model(image.unsqueeze(0).cuda())\n",
    "    output_argmax = output.detach().permute(1, 0, 2).argmax(dim=-1)\n",
    "    print('pred:', decode(output_argmax[0]))\n",
    "to_pil_image(image)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-18T12:49:28.691803Z",
     "start_time": "2019-06-18T12:49:28.645668Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ypw/anaconda3/lib/python3.6/site-packages/torch/serialization.py:256: UserWarning: Couldn't retrieve source code for container of type Model. It won't be checked for correctness upon loading.\n",
      "  \"type \" + obj.__name__ + \". It won't be checked \"\n"
     ]
    }
   ],
   "source": [
    "torch.save(model, 'ctc.pth')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
